package emas.salb1;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import salb1.*;
import common.GeneticHelper;
import common.GeneticOperations;
import emas.inf.*;
import emas.impl.*;


public class Salb1Runner {
	private static final int _StepsNum = 20000000;	//liczba krokow algorytmu (2000)
	private static final int _AgentNo = 100; //poczatkowa liczba agentow 200
	private static final int _IslandNo = 2; //liczba wysp w systemie

	//private static final int _maxEnergy = 0; //maksymalna poczatkowa energia wyspy
	private static final int _startAgentEnergy = 3; //poczatkowa energia agenta
	private static final int _DieThreshold = 0;	//prog smierci
	private static final int _ReproductionThreshold = _startAgentEnergy + _DieThreshold + 1;	//prog reprodukcji
	private static final int _BasicEnergyQuantum = 1;	//podstawowe quantum energii
	private static int _EnvEnergy = 0;

	private ArrayList<Island<Salb1Genotype>> _islands;
	private GeneticOperations<Salb1Genotype> go;

	private static int currentStepNo = 0;


	//20ms
	private final double timeLimit = 15 * 1000; 
	public double ref_f0 = 13.0;
	public double ref_f1 = 13.0;
	private long begMili;
	private long endMili;



	public Salb1Runner(GeneticOperations<Salb1Genotype> aGo) {
		this.go = aGo;
		_islands = new ArrayList<Island<Salb1Genotype>>();
	}


	public void initEnv() {
		Random rand = new Random();

		for(int i = 0; i < _IslandNo; i++) {
			_islands.add(new EmasIsland<Salb1Genotype>(0));
		}

		//utworzenie nowych agentow
		for(int i = 0; i < _AgentNo; i++) {
			List<Island<Salb1Genotype>> islandsTmp = new ArrayList<Island<Salb1Genotype>>();

			int IslandNum = rand.nextInt(_IslandNo);
			for (Island<Salb1Genotype> island: _islands) {
				if(island != _islands.get(IslandNum))
					islandsTmp.add(island);
			}

			@SuppressWarnings("unused")
			EmasAgent<Salb1Genotype> agent = new EmasAgent<Salb1Genotype>(go.createRandomGenotype(), go, _islands.get(IslandNum), islandsTmp, Species.valueOf("NORMAL"), Sex.valueOf("UNISEX"), _ReproductionThreshold, _DieThreshold, _startAgentEnergy, _BasicEnergyQuantum);
			EmasAgent.srodowisko++;
		}

		//przekopiowanie list z nowymi agentami
		for (Island<Salb1Genotype> island: _islands) {
			island.copyList();
		}
	}


	public void steps() {
		//kolejne kroki zycia agentow
		for (Island<Salb1Genotype> island: _islands) {	
			for(Agent<Salb1Genotype> agent: island.getAgents()) { 
				agent.LifeStep();
			}
			Salb1Runner._EnvEnergy += island.receiveEnergy();
		}

		this.tryCreateRandomIndividuals();

		for (Island<Salb1Genotype> island: _islands) {
			island.copyList();	
		}

		currentStepNo++;

		/*if((currentStepNo % 20) == 0) {
			Random rand = new Random();
			List<Island<Salb1Genotype>> islandsTmp = new ArrayList<Island<Salb1Genotype>>();
			int IslandNum = rand.nextInt(_IslandNo);
			for (Island<Salb1Genotype> island: _islands) {
				if(island != _islands.get(IslandNum))
					islandsTmp.add(island);
			}
			@SuppressWarnings("unused")
			EmasAgent<Salb1Genotype> agent = new EmasAgent<Salb1Genotype>(go.createRandomGenotype(), go, _islands.get(IslandNum), islandsTmp, Species.valueOf("NORMAL"), Sex.valueOf("UNISEX"), _ReproductionThreshold, _DieThreshold, _startAgentEnergy, _BasicEnergyQuantum);
			EmasAgent.srodowisko++;
		}*/
	}


	public void tryCreateRandomIndividuals() {
		while(Salb1Runner._EnvEnergy >= Salb1Runner._startAgentEnergy) {
			Random rand = new Random();

			Salb1Runner._EnvEnergy -= Salb1Runner._startAgentEnergy;

			List<Island<Salb1Genotype>> islandsTmp = new ArrayList<Island<Salb1Genotype>>();
			int IslandNum = rand.nextInt(_IslandNo);
			for (Island<Salb1Genotype> island: _islands) {
				if(island != _islands.get(IslandNum))
					islandsTmp.add(island);
			}

			@SuppressWarnings("unused")
			EmasAgent<Salb1Genotype> agent = new EmasAgent<Salb1Genotype>(go.createRandomGenotype(), go,(Island<Salb1Genotype>)_islands.get(IslandNum), islandsTmp, Species.valueOf("NORMAL"), Sex.valueOf("UNISEX"), _ReproductionThreshold, _DieThreshold, _startAgentEnergy, _BasicEnergyQuantum);
			EmasAgent.srodowisko++;
		}
	}

	public List<Agent<Salb1Genotype>> getAllAgents() {

		List<Agent<Salb1Genotype>> allAgents = new ArrayList<Agent<Salb1Genotype>>();
		for (Island<Salb1Genotype> island : this._islands) {
			allAgents.addAll(island.getAgents());
		}
		return allAgents;
	}


	public static void runExperiment(String input, String output, String hvfile) {		
		TimeOfOperations top = null;
		try {
			top = new TimeOfOperations(input);
		}
		catch (Exception e) { e.printStackTrace(); }

		Salb1GeneticOperations go = new Salb1GeneticOperations(top);
		Salb1Runner er = new Salb1Runner(go);
		er.begMili = System.currentTimeMillis();
		er.initEnv();
		int j = 0;
		int i;
		List<Double> values;


		// zapisanie losowych wynikow do pliku testowego poczatekowej populacji
		/*try {
			BufferedWriter out2 = new BufferedWriter(new FileWriter("random.txt"));
			for(Agent<Salb1Genotype> agent: er._islands.get(0).getAgents()) {

				out2.write(agent.getValues().get(0).toString());
				out2.write(' ');
				out2.write(agent.getValues().get(1).toString());
				out2.newLine();
			}
			for(Agent<Salb1Genotype> agent: er._islands.get(1).getAgents()) {

				out2.write(agent.getValues().get(0).toString());
				out2.write(' ');
				out2.write(agent.getValues().get(1).toString());
				out2.newLine();

			}
			out2.close();
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}*/
		// koniec :)



		BufferedWriter HVout = null;
		try {
			HVout = new BufferedWriter(new FileWriter(hvfile));

			it_loop:
				for(i = 0; i < _StepsNum; i++)
				{
					er.steps();

					List<Agent<Salb1Genotype>> tmpAgents;
					tmpAgents = er.getAllAgents();

					if (i % 200 == 0) {
						double HV = GeneticHelper.calculateHiperValume(tmpAgents, er.ref_f0, er.ref_f1);
						long time = System.currentTimeMillis() - er.begMili;
						//System.out.println("time " + time + " step "+ i + " HV = " + HV);
						try {
							HVout.write(String.valueOf(time));
							//HVout.write(String.valueOf(i));
							HVout.write(' ');
							HVout.write(String.valueOf(HV));					
							HVout.newLine();
						} catch (IOException e) {
							System.out.println("IOException " + e.getMessage());
						}

						if (time > er.timeLimit) {
							System.out.println("Czas sie skonczyl");
							break it_loop;
						}
					}
				}
			er.endMili = System.currentTimeMillis() - er.begMili;
			System.out.println("Czas wykonania: " + er.endMili + " w liczbie krokow " + i);


			if (i == _StepsNum) {
				System.out.println("Wykonano wszystkie kroki");
			}

			er.endMili = System.currentTimeMillis();
			//er.calc_normalHV = calculateHV(er.getAllAgents(),er.ref_f0,er.ref_f1);
			//er.calc_normalHVR = this.calc_normalHV / this.calc_optimalHV;

			HVout.close();
		} catch (IOException e1) {
			System.out.println("IOException " + e1.getMessage());
		}


		//rozw to genotypy wszystkich agentow we wszystkich wyspach
		try {
			/*System.out.println("!!!Liczba wysp " + er._islands.size());
			System.out.println("!!!Liczba agentow na 1. wyspie " + er._islands.get(0).getAgents().size());
			System.out.println("!!!Liczba agentow na 2. wyspie " + er._islands.get(1).getAgents().size());*/

			BufferedWriter out = new BufferedWriter(new FileWriter(output));
			for (Island<Salb1Genotype> island: er._islands) {
				for(Agent<Salb1Genotype> agent: island.getAgents()) {
					//wypisanie rozw
					values = agent.getValues();
					out.write(String.valueOf(values.get(0)));
					out.write(' ');
					out.write(String.valueOf(values.get(1)));
					out.newLine();

					j++;
					System.out.println(values.get(0) + " " + values.get(1));
				}
			}
			out.close();
		} catch (IOException e) {
			System.out.println(e);
		}

		System.out.println();
		System.out.println("Liczebnosc populacji: " + j);
		System.out.println();

		//statystyki
		System.out.println();
		System.out.println("Urodzen: " + EmasAgent.powstalo);
		System.out.println("W tym przez srodowisko: " + EmasAgent.srodowisko);
		System.out.println("Zgonow: " + EmasAgent.umarlo);
		System.out.println("Spotkan: " + EmasAgent.spotkalo);
		System.out.println("Reprodukcji: " + EmasAgent.stosunki);
		System.out.println("Migracji: " + EmasAgent.migracje);

	}

	public Salb1Runner() {
		_islands = new ArrayList<Island<Salb1Genotype>>();
	}

	public static void main(String[] args) {
		Salb1Runner.runExperiment("input_20_40_20.txt", "wynik_emas.txt", "hv_emas.txt");
	}
}
